package edu.unicen.seo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import edu.unicen.seo.utils.PersistenceData;

public class SeoTandilActivity extends Activity {

	/**
	 * Nombre del archivo en donde se guardan los últimos dominios utilizados. 
	 */
	private static final String LAST_DOMAINS_FILE = "lastDomainsFile.dat";

	/**
	 * Nombre del archivo donde se guarda la configuración. 
	 */
	private static final String SETTINGS_FILE = "settings.dat";

	public static final String ACTION_SMS_SENT = "edu.unicen.seo.SMS_SENT_ACTION";
	
	/**
	 * Cantidad de dominios que se guardan en el archiivo. 
	 */
	private static final int CACHE_MAX_SIZE = 5;

	private String[] lastDomains;

	private AutoCompleteTextView domainAutoCompleteTextView;

	private EditText addressTextEdit;

	private Spinner timeSpinner;

	private EditText messageTextEdit;

	private Button sendButton;

	private TextView statusView;

	/**
	 * Mapa que contiene como key el nombre de la calle y como value el código
	 * de la calle correspondiente en el sistema de estacionamiento ordenado.
	 */
	private static final HashMap<String, String> mapping = new HashMap<String, String>() {
		private static final long serialVersionUID = 1L;
		{
			put("Gral. Paz", "PA");
			put("Leandro N. Alem", "AL");
			put("9 de Julio", "NU");
			put("Gral. Rodríguez", "RO");
			put("Independencia", "FU");
			put("Hipólito Irigoyen", "YR");
			put("Chacabuco", "CH");
			put("14 de Julio", "CA");
			put("Av España", "ES");
			put("Bartolomé Mitre", "MI");
			put("Sarmiento", "SA");
			put("San Martin", "SM");
			put("Gral. Pinto", "PI");
			put("Belgrano", "BE");
			put("Maipú", "MA");
		}
	};

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		inicializeViewComponents();
		updateAddressLocation();
	}

	@Override
	public void onStart() {
		super.onStart();
		loadLastDomains();
	}

	@Override
	public void onResume() {
		super.onResume();
		addListenerSendButton();
		addListenerDomain();
		cleanDomains();
		updateAddress();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.settings:

			final AlertDialog.Builder alert = new AlertDialog.Builder(
					SeoTandilActivity.this)
					.setTitle(R.string.smsRecipientTitle);

			String smsRecipient = PersistenceData.readData(
					SeoTandilActivity.this, SETTINGS_FILE).trim();
			final EditText smsRrecipientEditText = new EditText(this);
			smsRrecipientEditText.setLines(1);
			smsRrecipientEditText.setInputType(DEFAULT_KEYS_DIALER);
			smsRrecipientEditText.setText(smsRecipient);
			alert.setView(smsRrecipientEditText);

			alert.setPositiveButton("Ok",
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog,
								int whichButton) {

							PersistenceData.writeData(getApplicationContext(),
									smsRrecipientEditText.getText().toString()
											.trim(), SETTINGS_FILE);

							Toast.makeText(getApplicationContext(),
									R.string.savedSettings, Toast.LENGTH_SHORT)
									.show();
						}
					});

			alert.setNegativeButton("Cancelar",
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog,
								int whichButton) {
							dialog.cancel();
						}
					});

			alert.show();

			return true;
		case R.id.about:
			final AlertDialog alertDialog = new AlertDialog.Builder(
					SeoTandilActivity.this)
					.setTitle(R.string.appNameAndVersion)
					.setIcon(R.drawable.ic_launcher)
					.setMessage(R.string.aboutMessage).create();

			alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					dialog.cancel();
				}
			});
			alertDialog.show();

			return true;
		case R.id.exit:
			System.exit(0);
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	/**
	 * Inicializa los componentes visuales.
	 */
	private void inicializeViewComponents() {
		domainAutoCompleteTextView = (AutoCompleteTextView) SeoTandilActivity.this
				.findViewById(R.id.domain);

		addressTextEdit = (EditText) SeoTandilActivity.this
				.findViewById(R.id.address);

		timeSpinner = (Spinner) findViewById(R.id.timeSpinner);
		ArrayAdapter<CharSequence> adapter = ArrayAdapter
				.createFromResource(this, R.array.times_array,
						android.R.layout.simple_spinner_item);
		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		timeSpinner.setAdapter(adapter);

		messageTextEdit = (EditText) SeoTandilActivity.this
				.findViewById(R.id.message);

		sendButton = (Button) findViewById(R.id.sendButton);

		statusView = (TextView) SeoTandilActivity.this
				.findViewById(R.id.sms_status);
	}

	/**
	 * Pone en blanco el AutoCompleteTextView correspondiente al dominios.
	 */
	private void cleanDomains() {
		Button cleanDomainsButton = (Button) findViewById(R.id.cleanDomains);

		cleanDomainsButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				domainAutoCompleteTextView.setText("");
			}
		});
	}

	/**
	 * Actualiza la Calle y la Altura.
	 */
	private void updateAddress() {
		Button updateAddressButton = (Button) findViewById(R.id.updateAddress);

		updateAddressButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				updateAddressLocation();
			}
		});
	}

	private void addListenerDomain() {
		domainAutoCompleteTextView.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				domainAutoCompleteTextView.setDropDownAnchor(1);
				domainAutoCompleteTextView.showDropDown();
			}
		});
	}

	/**
	 * Completa los datos de los últimos dominios utilizados.
	 */
	private void loadLastDomains() {

		String lastDomainsFromFile = PersistenceData.readData(this,
				LAST_DOMAINS_FILE);
		lastDomains = lastDomainsFromFile.split(",");

		ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_dropdown_item_1line, lastDomains);

		domainAutoCompleteTextView.setAdapter(arrayAdapter);
	}

	/**
	 * Define y ejecuta la funcionalidad del botón que envía el mensaje.
	 */
	private void addListenerSendButton() {

		sendButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if (!isValidInputFormat(domainAutoCompleteTextView.getText()
						.toString(), 3, 3)) {
					Toast.makeText(SeoTandilActivity.this, R.string.badDomain,
							Toast.LENGTH_LONG).show();
					domainAutoCompleteTextView.setText("");
					return;
				}

				if (!isValidInputFormat(addressTextEdit.getText().toString(),
						2, 3)) {
					Toast.makeText(SeoTandilActivity.this, R.string.badAddress,
							Toast.LENGTH_LONG).show();
					addressTextEdit.setText("");
					return;
				}

				StringBuffer message = new StringBuffer();

				message.append(domainAutoCompleteTextView.getText().toString()
						.toUpperCase());
				message.append(" ");
				message.append(addressTextEdit.getText().toString()
						.toUpperCase());
				message.append(" ");
				message.append(timeSpinner.getSelectedItem().toString());

				messageTextEdit.setText(message);

				SmsManager smsManager = SmsManager.getDefault();

				String smsRecipient = PersistenceData.readData(
						SeoTandilActivity.this, SETTINGS_FILE).trim();

				if (smsRecipient != null && !"".equals(smsRecipient)) {
					List<String> messages = smsManager.divideMessage(message
							.toString());
					for (String messagePart : messages) {
						smsManager.sendTextMessage(smsRecipient, null,
								messagePart, PendingIntent.getBroadcast(
										SeoTandilActivity.this, 0, new Intent(
												ACTION_SMS_SENT), 0), null);
					}
					saveDomains(domainAutoCompleteTextView.getText().toString()
							.toUpperCase());
					domainAutoCompleteTextView.setText("");
					addressTextEdit.setText("");

				} else {
					Toast.makeText(SeoTandilActivity.this,
							R.string.nullSmsRecipient, Toast.LENGTH_LONG)
							.show();
				}
			}
		});

		registerReceiver(new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
				String message = null;
				boolean error = true;
				switch (getResultCode()) {
				case Activity.RESULT_OK:
					message = "SMS Enviado.";
					error = false;
					break;
				case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
					message = "Error.";
					break;
				case SmsManager.RESULT_ERROR_NO_SERVICE:
					message = "Error: Servicio no disponible.";
					break;
				case SmsManager.RESULT_ERROR_NULL_PDU:
					message = "Error: PDU nulo.";
					break;
				case SmsManager.RESULT_ERROR_RADIO_OFF:
					message = "Error: Radio off.";
					break;
				}

				statusView.setText(message);
				statusView.setTextColor(error ? Color.RED : Color.GREEN);
			}
		}, new IntentFilter(ACTION_SMS_SENT));

	}

	/**
	 * Actualiza la ubicación.
	 */
	private void updateAddressLocation() {

		LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

		LocationListener locationListener = new LocationListener() {

			@Override
			public void onLocationChanged(android.location.Location location) {
			}

			@Override
			public void onProviderDisabled(String provider) {
			}

			@Override
			public void onProviderEnabled(String provider) {
			}

			@Override
			public void onStatusChanged(String provider, int status,
					Bundle extras) {				
			}
		};

		Location location = null;

		List<String> enabledProviders = locationManager.getProviders(true);

		for (String provider : enabledProviders) {
			if (location == null) {
				locationManager.requestLocationUpdates(provider, 5, 20f,
						locationListener);
				location = locationManager.getLastKnownLocation(provider);
			} else {
				break;
			}
		}

		List<Address> addresses = new ArrayList<Address>();

		StringBuffer address = new StringBuffer();

		if (location != null) {
			Geocoder geocoder = new Geocoder(getApplicationContext(),
					Locale.getDefault());
			try {
				
				addresses = geocoder.getFromLocation(location.getLatitude(),
						location.getLongitude(), 1);

				if (addresses.size() == 0) {
					Toast.makeText(this, R.string.noDataFound,
							Toast.LENGTH_LONG);
				} else {
					final int length = addresses.get(0).getAddressLine(0)
							.length();

					String street = addresses.get(0).getAddressLine(0)
							.substring(0, length - 8);
					String block = addresses.get(0).getAddressLine(0)
							.substring(length - 7, length - 4);

					address.append(SeoTandilActivity.mapping.get(street));
					address.append(" ");
					address.append(block);
				}

			} catch (Exception e) {
				Toast.makeText(SeoTandilActivity.this, R.string.noDataFound,
						Toast.LENGTH_SHORT).show();
				address.append("No Inf");
			}
		} else {
			Toast.makeText(SeoTandilActivity.this, R.string.noDataFound,
					Toast.LENGTH_SHORT).show();
			address.append("No Inf");
		}
		locationManager.removeUpdates(locationListener);
		
		addressTextEdit.setText(address);
	}

	/**
	 * Ordena y almacena las ultimas patentes utilizadas. El criterio del orden
	 * es posicionar en primer lugar la ultima patente utilizada.
	 * 
	 * @param currentDomain
	 */
	private void saveDomains(String currentDomain) {

		String stringFromFile = PersistenceData.readData(this,
				LAST_DOMAINS_FILE);
		String[] savedDomains = stringFromFile.split(",");

		List<String> sd = new ArrayList<String>();
		for (String string : savedDomains) {
			if (!string.equals("")) {
				sd.add(string);
			}
		}
		sd.remove(currentDomain);
		sd.add(0, currentDomain);

		StringBuffer domainsToFile = new StringBuffer();

		if (sd.size() > CACHE_MAX_SIZE) {
			sd = sd.subList(0, CACHE_MAX_SIZE);
		}

		for (Iterator<String> iterator = sd.iterator(); iterator.hasNext();) {
			String string = (String) iterator.next();

			domainsToFile.append(string);
			if (iterator.hasNext()) {
				domainsToFile.append(",");
			}
		}

		PersistenceData.writeData(getApplicationContext(),
				domainsToFile.toString(), LAST_DOMAINS_FILE);

		lastDomains = domainsToFile.toString().split(",");

		ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_dropdown_item_1line, lastDomains);

		domainAutoCompleteTextView.setAdapter(arrayAdapter);
	}

	/**
	 * Valida el formato de los dominios y de las calles y alturas.
	 * 
	 * @param input
	 *            Cadena de caracteres a validar.
	 * @param letters
	 *            Entero que representa la cantidad de caracteres del tipo
	 *            alfabético.
	 * @param numbers
	 *            Entero que representa la cantidad de caracteres del tipo
	 *            numérico.
	 * @return
	 */
	private boolean isValidInputFormat(String input, int letters, int numbers) {

		String[] splitedInput = input.split(" ");

		if (splitedInput.length != 2) {
			return false;
		}

		if (!(splitedInput[0].matches("[a-zA-Z]{" + letters + "}"))) {
			return false;
		}

		if (!(splitedInput[1].matches("[0-9]{" + numbers + "}"))) {
			return false;
		}
		return true;
	}
}
